/* //-------------------------------------------------------------------------
//
// polyroots.hpp/polyroots.cpp
//
// Utility functions to solve low-order polynomial equations efficiently
// and robustly.  Very useful when writing ray-object intersection tests.
// You don't need these functions in Assignment 3.
//
//------------------------------------------------------------------------- */

/*
** File: PolyRoots.c
** Purpose: Find the roots of polynomials of degree 4 or less.
** Author:  James Painter
** Last Modified: 27 January 1988
**
**  Notes:  Stability of the solution is a major consideration here.
**  See:      ``Solving Quartics and Cubics for Graphics'',
**            Don Herbison-Evans
**            University of Waterloo Research Report
**            CS-86-56,  November, 1986
**
** Copyright (c), 1987 GRAIL, University of Washington
**
** $Revision: 1.2 $
** $Date: 2004/04/29 20:20:08 $
** $Locker:  $
** $Log: polyroots.c,v $
** Revision 1.2  2004/04/29 20:20:08  csk
** *** empty log message ***
**
** Revision 1.1  2004/04/29 20:13:54  csk
** *** empty log message ***
**
** Revision 1.1  2004/04/23 20:35:31  csk
** *** empty log message ***
**
** Revision 1.1  2004/02/19 04:10:05  csk
** *** empty log message ***
**
** Revision 1.1  2003/12/03 22:35:00  csk
** HYello.
**
** Revision 1.2  2003/07/07 13:17:19  csk
** Things.
**
** Revision 1.1  2003/07/05 03:54:18  csk
** Whatever.
**
** Revision 1.1  2003/04/02 03:20:23  csk
** Things.
**
** Revision 1.1  2003/03/28 22:09:03  csk
** *** empty log message ***
**
 * Revision 2.0  88/04/29  16:07:19  gem
 * *** empty log message ***
 * 
 * Revision 1.4  88/04/28  12:42:51  jamie
 * *** empty log message ***
 * 
 * Revision 1.3  88/02/01  18:41:46  jamie
 * Total rewrite to improve speed and stability.
 * 
*/

/* Imports */
#include <stdlib.h>
#include <math.h>

/* Forward declarations */
double sink_lookup(double), cosk_lookup(double);
static double PolishRoot( 
	size_t degree, double A, double B, double C, double D, double root );
#define fabs(x) ( ((x) > 0) ? x : -x )

/* The square root of 3:  */
#define SQRT3 1.732050807568878

/* SIGN(x) is -1 if x is negative, otherwise it's 1 */
#define SIGN(x) (((x) < 0) ? -1 : 1)

/*
 * Hack together a Windows equivalent for cube root.  It's defined 
 * in Linux.  This is probably not the best way to implement the cube
 * root function.
 */
// Comment this out: it is defined in Windows now.
//#ifdef _WIN32
//static double cbrt( double t )
//{
//	return pow( t, 1.0/3.0 );
//}	
//#endif

/*
**  Return the real roots of a quadratic polynomial over the reals.
**   Reference:  ``Quadratic and Cubic Equations'',  Numerical Recipes
**               pp 145-146.
**    Note that the method used here is more stable the the standard
**    -b +- (b^2 - 4ac)/ 2a  when b and the discriminant are of similar
**    magnitude.
*/
size_t quadraticRoots( double A, double B, double C, double roots[2] )
{
	double D;
	double q;

	if( A == 0 ) {
		if( B == 0 ) {
			return 0;
		} else {	
			roots[0] = -C/B;
			return 1;
		}
	} else {
		/*  Compute the discrimanant D=b^2 - 4ac */
		D = B*B - 4*A*C;
		if( D < 0 ) {
			return 0;
		} else {
			/* Two real roots */
			q = -( B + SIGN(B)*sqrt(D) ) / 2.0;
			roots[0] = q / A;
			if( q != 0 ) {
				roots[1] = C / q;
			} else {
				roots[1] = roots[0];
			}
			return 2;
		}
	}
}

/*
**  Return the real roots of a monic cubic polynomial over the reals.
**  Reference: ``Solving Quartics and Cubics for Graphics'',
**            Don Herbison-Evans
**            University of Waterloo Research Report
**            CS-86-56,  November, 1986
**    Note:  The accuracy of the root can be improved with "PolishRoot".
*/
/* Coefficients :   x^3  + C[0] x^2 + C[1] x  + C[2] */
size_t cubicRoots( double p, double q, double r, double roots[3] )
{
  register double u, v, w, s, t, cosk, sink, p_over_3, k;
  /* int i; */

  u = q - p*p/3.0;
  v = r - p*q/3 + 2*p*p*p/27;

  /* Better stability could be gained by expanded out the expression
  ** for w in terms of p,q and r and summing in increasing order of 
  ** magnitude.  For now, we won't bother.
  */
  w = (4*u*u*u)/27 + v*v;

  if (w > 0)  {			/* One real root */
    w = sqrt(w);
    if (v < 0) {
      double w_minus_v = w - v;
      roots[0] = cbrt(w_minus_v/2.0) - (u*cbrt(2.0/w_minus_v) + p)  / 3.0;
    } else {
      double w_plus_v = w + v;
      roots[0] = - cbrt(w_plus_v/2.0) + (u*cbrt(2.0/w_plus_v) - p) / 3.0;
    }
	return 1;
  } else {			/* Three real roots */
    s = sqrt( -u / 3 );
    if (s != 0) {
      t = -v / (2.0 * s*s*s);
#ifdef TABLE_LOOKUP      
      cosk = cosk_lookup( t  );
      sink = sink_lookup( t );
#else
      k = acos(t)/3.0;
      sink = sin(k);
      cosk = cos(k);
#endif
    } else
      cosk = sink = 0;
    p_over_3 = p / 3.0;
    roots[0] = 2.0 * s * cosk - p_over_3;
    roots[1] = 2.0 * s * (-cosk + SQRT3*sink)/2.0 - p_over_3;
    roots[2] = 2.0 * s * (-cosk - SQRT3*sink)/2.0 - p_over_3;
	return 3;
  }
}

/*
**  Return the real roots of a monic quartic polynomial over the reals.
**  Reference: ``Solving Quartics and Cubics for Graphics'',
**            Don Herbison-Evans
**            University of Waterloo Research Report
**            CS-86-56,  November, 1986
*/
/* Coefficients :  x^4 + C[0] x^3  + C[1] x^2 + C[2] x  + C[3] */
size_t quarticRoots( 
	double a, double b, double c, double d, double roots[4] )
{
	double h,h1,h2,H,   g,g1,g2,G, n, m, en, em, y;
	double cubic[3];	/* Cubic and quadratic coefficients */
	int i, nr;

	/* Find the a real root of a certain cubic */
	cubic[0] = -2.0*b;
	cubic[1] = b*b + a*c - 4*d;
	cubic[2] = c*c - a*b*c + a*a*d;
	nr = cubicRoots( cubic[0], cubic[1], cubic[2], roots );

	if( nr == 1 ) {
		y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[0] );
	} else {
		if( b < 0 && d < 0 ) {
			y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[2] );
		} else {
			y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[0] );
		}
	}

	g1 = a/2.0;
	h1 = (b-y)/2.0;
	if( y < 0 ) {
		n = a*a - 4*y;
		if( n <= 0 ) {
			return 0;
		}
		g2 = sqrt(n);
		if( g2 == 0 ) {
			return 0;
		}
		h2 = (a*((b-y)/2.0) - c) / g2;
		g2 /= 2.0;
	} else if( y > 0 && d > 0 && b < 0 ) {
		m = (b-y)*(b-y) - 4*d;
		if( m <= 0 ) {
			return 0;
		}
		h2 = sqrt(m);
		if( h2 == 0 ) {
			return 0;
		}
		g2 = (a*h1 - c) / h2;
		h2 /= 2.0;
	} else {
		n = a*a - 4*y;
		m = (b-y)*(b-y) - 4*d;
		en = b*b + 2.*fabs(b*y) + y*y + 4*fabs(d);
		em = a*a + 4.*fabs(y);
		if( m*en > n*em ) {		/* use m */
			if( m <= 0 ) { 
				return 0; 
			}
			h2 = sqrt(m);
			if( h2 == 0 ) { 
				return 0; 
			}
			g2 = (a*h1 - c) / h2;
			h2 /= 2.0;
		} else {			/* use n */
			if (n <= 0) {
				return 0;
			}
			g2 = sqrt(n);
			if (g2 == 0) { 
				return 0; 
			}
			h2 = (a*( (b-y)/2.0) - c) / g2;
			g2 /= 2.0;
		}
	}

	if( SIGN(g1) == SIGN(g2) ) {
		G = g1 + g2;
		g = (G==0) ? g1-g2  : y/G;
	} else {
		g = g1 - g2;
		G = (g == 0) ?  g1+g2 : y/g;
	}
	if( SIGN(h1) == SIGN(h2) ) {
		H = h1+h2;
		h = (H == 0) ? h1-h2  : d/H;
	} else {
		h = h1 - h2;
		H = (h == 0) ? h1+h2  : d/h;
	}

	nr = quadraticRoots( 1.0, G, H, roots );
	nr += quadraticRoots( 1.0, g, h, roots+nr );

	for( i=0; i < nr; ++i ) {
		roots[i] = PolishRoot( 4, a, b, c, d, roots[i] );
	}

	return nr;
}

/*  Polish a monic polynomial root by Newton-Raphson iteration */
/* degree <= 4; c[] has 'degree' values. */
static double PolishRoot( 
	size_t degree, double A, double B, double C, double D, double root )
{
	size_t i, j;
	double x, y, dydx, dx, lastx = HUGE, lasty = HUGE;
	double cs[4] = { A, B, C, D };

	x = root;

	for( i = 0; i < 3; ++i ) {  /* Up to 3 iterations */
		/* Evaluate the polynomial and its derivative */
		y = 1;
		dydx = 0;

		for( j = 0; j < degree; ++j ) {
			dydx = dydx*x + y;
			y *= x;
			y += cs[ j ];
		}

		if( dydx == 0 ) {
			break;	/* Bad news */
		}

		if( fabs(y) > fabs(lasty) ) {
			x = lastx;
			break;			/* Not converging */
		}

		lasty = y;

		/* Form a new estimate for the root */
		dx = y/dydx;
		lastx = x;
		x -= dx;
		if( lastx == x ) {
			break;  /* Can't do any better */
		}
	}

	return x;
}

#ifdef TABLE_LOOKUP
static double cosk_lookup(double t)
{
  register int it;
  register double tprime, low, high, u, v;
  static double table[1025] = {	/* Precalculated table of cos( arccos(t)/3 ) */
    0.500000000000,0.517935289657,0.525302996762,0.530932683914,0.535662933895,
    0.539818532924,0.543566035609,0.547004373987,0.550198001933,0.553191685247,
    0.556018014738,0.558701591651,0.561261527021,0.563713017516,0.566068383456,
    0.568337777666,0.570529684398,0.572651279653,0.574708697244,0.576707229087,
    0.578651478568,0.580545479760,0.582392791344,0.584196571504,0.585959638301,
    0.587684518833,0.589373489616,0.591028610035,0.592651750268,0.594244614749,
    0.595808762027,0.597345621661,0.598856508681,0.600342636033,0.601805125329,
    0.603245016198,0.604663274427,0.606060799099,0.607438428869,0.608796947495,
    0.610137088740,0.611459540722,0.612764949791,0.614053923992,0.615327036167,
    0.616584826744,0.617827806244,0.619056457548,0.620271237946,0.621472580993,
    0.622660898201,0.623836580571,0.625000000000,0.626151510559,0.627291449666,
    0.628420139161,0.629537886297,0.630644984642,0.631741714925,0.632828345800,
    0.633905134565,0.634972327821,0.636030162084,0.637078864352,0.638118652633,
    0.639149736433,0.640172317218,0.641186588832,0.642192737899,0.643190944193,
    0.644181380983,0.645164215359,0.646139608534,0.647107716132,0.648068688451,
    0.649022670719,0.649969803327,0.650910222054,0.651844058270,0.652771439144,
    0.653692487818,0.654607323591,0.655516062084,0.656418815391,0.657315692233,
    0.658206798099,0.659092235373,0.659972103465,0.660846498929,0.661715515578,
    0.662579244588,0.663437774603,0.664291191833,0.665139580144,0.665983021148,
    0.666821594286,0.667655376909,0.668484444351,0.669308870005,0.670128725393,
    0.670944080226,0.671755002475,0.672561558427,0.673363812742,0.674161828511,
    0.674955667307,0.675745389237,0.676531052987,0.677312715873,0.678090433882,
    0.678864261717,0.679634252834,0.680400459488,0.681162932761,0.681921722609,
    0.682676877889,0.683428446394,0.684176474886,0.684921009128,0.685662093911,
    0.686399773084,0.687134089581,0.687865085449,0.688592801871,0.689317279191,
    0.690038556940,0.690756673857,0.691471667910,0.692183576319,0.692892435576,
    0.693598281465,0.694301149079,0.695001072843,0.695698086525,0.696392223261,
    0.697083515564,0.697771995347,0.698457693933,0.699140642074,0.699820869961,
    0.700498407244,0.701173283041,0.701845525952,0.702515164074,0.703182225010,
    0.703846735884,0.704508723351,0.705168213608,0.705825232406,0.706479805059,
    0.707131956456,0.707781711071,0.708429092971,0.709074125825,0.709716832916,
    0.710357237147,0.710995361052,0.711631226801,0.712264856212,0.712896270755,
    0.713525491562,0.714152539436,0.714777434852,0.715400197973,0.716020848647,
    0.716639406421,0.717255890547,0.717870319982,0.718482713401,0.719093089200,
    0.719701465501,0.720307860160,0.720912290770,0.721514774668,0.722115328941,
    0.722713970426,0.723310715723,0.723905581193,0.724498582966,0.725089736943,
    0.725679058805,0.726266564012,0.726852267811,0.727436185239,0.728018331126,
    0.728598720101,0.729177366593,0.729754284837,0.730329488879,0.730902992574,
    0.731474809595,0.732044953435,0.732613437409,0.733180274658,0.733745478152,
    0.734309060693,0.734871034920,0.735431413307,0.735990208171,0.736547431675,
    0.737103095824,0.737657212476,0.738209793339,0.738760849976,0.739310393808,
    0.739858436115,0.740404988037,0.740950060581,0.741493664619,0.742035810893,
    0.742576510013,0.743115772466,0.743653608612,0.744190028688,0.744725042812,
    0.745258660983,0.745790893081,0.746321748874,0.746851238015,0.747379370048,
    0.747906154404,0.748431600410,0.748955717285,0.749478514144,0.750000000000,
    0.750520183764,0.751039074249,0.751556680169,0.752073010140,0.752588072687,
    0.753101876238,0.753614429131,0.754125739612,0.754635815838,0.755144665880,
    0.755652297719,0.756158719253,0.756663938295,0.757167962576,0.757670799746,
    0.758172457371,0.758672942942,0.759172263870,0.759670427489,0.760167441058,
    0.760663311760,0.761158046706,0.761651652932,0.762144137405,0.762635507019,
    0.763125768601,0.763614928907,0.764102994627,0.764589972384,0.765075868734,
    0.765560690170,0.766044443119,0.766527133946,0.767008768955,0.767489354386,
    0.767968896420,0.768447401178,0.768924874722,0.769401323056,0.769876752127,
    0.770351167824,0.770824575981,0.771296982377,0.771768392737,0.772238812731,
    0.772708247977,0.773176704040,0.773644186435,0.774110700624,0.774576252020,
    0.775040845985,0.775504487834,0.775967182832,0.776428936196,0.776889753097,
    0.777349638658,0.777808597959,0.778266636030,0.778723757860,0.779179968391,
    0.779635272524,0.780089675113,0.780543180974,0.780995794876,0.781447521551,
    0.781898365685,0.782348331928,0.782797424886,0.783245649128,0.783693009182,
    0.784139509538,0.784585154649,0.785029948927,0.785473896750,0.785917002457,
    0.786359270351,0.786800704699,0.787241309733,0.787681089648,0.788120048605,
    0.788558190732,0.788995520121,0.789432040831,0.789867756888,0.790302672285,
    0.790736790982,0.791170116909,0.791602653960,0.792034406002,0.792465376867,
    0.792895570361,0.793324990255,0.793753640292,0.794181524185,0.794608645619,
    0.795035008247,0.795460615697,0.795885471564,0.796309579420,0.796732942804,
    0.797155565232,0.797577450191,0.797998601139,0.798419021512,0.798838714714,
    0.799257684128,0.799675933109,0.800093464986,0.800510283063,0.800926390620,
    0.801341790912,0.801756487169,0.802170482596,0.802583780376,0.802996383668,
    0.803408295605,0.803819519301,0.804230057842,0.804639914297,0.805049091707,
    0.805457593094,0.805865421458,0.806272579775,0.806679071002,0.807084898072,
    0.807490063899,0.807894571376,0.808298423372,0.808701622741,0.809104172311,
    0.809506074894,0.809907333279,0.810307950239,0.810707928523,0.811107270864,
    0.811505979975,0.811904058549,0.812301509261,0.812698334768,0.813094537707,
    0.813490120698,0.813885086343,0.814279437225,0.814673175911,0.815066304948,
    0.815458826867,0.815850744183,0.816242059391,0.816632774973,0.817022893390,
    0.817412417089,0.817801348501,0.818189690038,0.818577444099,0.818964613065,
    0.819351199302,0.819737205159,0.820122632972,0.820507485060,0.820891763726,
    0.821275471260,0.821658609935,0.822041182010,0.822423189729,0.822804635322,
    0.823185521004,0.823565848977,0.823945621426,0.824324840525,0.824703508432,
    0.825081627292,0.825459199237,0.825836226385,0.826212710838,0.826588654689,
    0.826964060015,0.827338928881,0.827713263339,0.828087065426,0.828460337170,
    0.828833080583,0.829205297667,0.829576990410,0.829948160787,0.830318810764,
    0.830688942292,0.831058557310,0.831427657747,0.831796245519,0.832164322529,
    0.832531890672,0.832898951828,0.833265507868,0.833631560649,0.833997112020,
    0.834362163816,0.834726717863,0.835090775976,0.835454339957,0.835817411600,
    0.836179992687,0.836542084989,0.836903690267,0.837264810272,0.837625446745,
    0.837985601415,0.838345276002,0.838704472217,0.839063191759,0.839421436318,
    0.839779207574,0.840136507199,0.840493336852,0.840849698185,0.841205592840,
    0.841561022448,0.841915988634,0.842270493010,0.842624537181,0.842978122742,
    0.843331251280,0.843683924372,0.844036143586,0.844387910481,0.844739226608,
    0.845090093510,0.845440512720,0.845790485761,0.846140014152,0.846489099399,
    0.846837743002,0.847185946452,0.847533711232,0.847881038817,0.848227930673,
    0.848574388259,0.848920413025,0.849266006414,0.849611169860,0.849955904791,
    0.850300212626,0.850644094776,0.850987552644,0.851330587628,0.851673201115,
    0.852015394487,0.852357169118,0.852698526374,0.853039467615,0.853379994191,
    0.853720107448,0.854059808724,0.854399099349,0.854737980646,0.855076453931,
    0.855414520515,0.855752181699,0.856089438780,0.856426293047,0.856762745781,
    0.857098798259,0.857434451749,0.857769707514,0.858104566809,0.858439030884,
    0.858773100981,0.859106778338,0.859440064184,0.859772959742,0.860105466232,
    0.860437584863,0.860769316841,0.861100663365,0.861431625628,0.861762204816,
    0.862092402111,0.862422218688,0.862751655715,0.863080714355,0.863409395767,
    0.863737701100,0.864065631502,0.864393188112,0.864720372064,0.865047184487,
    0.865373626504,0.865699699232,0.866025403784,0.866350741267,0.866675712780,
    0.867000319421,0.867324562278,0.867648442438,0.867971960980,0.868295118977,
    0.868617917501,0.868940357613,0.869262440374,0.869584166836,0.869905538049,
    0.870226555056,0.870547218896,0.870867530602,0.871187491203,0.871507101722,
    0.871826363179,0.872145276586,0.872463842954,0.872782063287,0.873099938584,
    0.873417469840,0.873734658045,0.874051504186,0.874368009241,0.874684174189,
    0.875000000000,0.875315487642,0.875630638077,0.875945452263,0.876259931155,
    0.876574075701,0.876887886846,0.877201365531,0.877514512691,0.877827329259,
    0.878139816163,0.878451974324,0.878763804664,0.879075308095,0.879386485529,
    0.879697337873,0.880007866029,0.880318070895,0.880627953365,0.880937514329,
    0.881246754674,0.881555675282,0.881864277031,0.882172560794,0.882480527443,
    0.882788177844,0.883095512859,0.883402533348,0.883709240164,0.884015634158,
    0.884321716179,0.884627487070,0.884932947670,0.885238098815,0.885542941338,
    0.885847476068,0.886151703829,0.886455625443,0.886759241729,0.887062553499,
    0.887365561565,0.887668266735,0.887970669811,0.888272771594,0.888574572881,
    0.888876074465,0.889177277136,0.889478181680,0.889778788880,0.890079099517,
    0.890379114365,0.890678834200,0.890978259789,0.891277391900,0.891576231295,
    0.891874778735,0.892173034977,0.892471000773,0.892768676874,0.893066064027,
    0.893363162977,0.893659974464,0.893956499225,0.894252737996,0.894548691507,
    0.894844360488,0.895139745663,0.895434847756,0.895729667485,0.896024205568,
    0.896318462716,0.896612439642,0.896906137052,0.897199555651,0.897492696141,
    0.897785559220,0.898078145585,0.898370455928,0.898662490941,0.898954251309,
    0.899245737719,0.899536950851,0.899827891385,0.900118559998,0.900408957361,
    0.900699084148,0.900988941024,0.901278528657,0.901567847709,0.901856898839,
    0.902145682705,0.902434199963,0.902722451263,0.903010437256,0.903298158588,
    0.903585615904,0.903872809846,0.904159741053,0.904446410161,0.904732817805,
    0.905018964617,0.905304851225,0.905590478257,0.905875846335,0.906160956084,
    0.906445808120,0.906730403063,0.907014741525,0.907298824119,0.907582651455,
    0.907866224140,0.908149542779,0.908432607975,0.908715420328,0.908997980435,
    0.909280288893,0.909562346294,0.909844153230,0.910125710290,0.910407018060,
    0.910688077124,0.910968888064,0.911249451460,0.911529767890,0.911809837929,
    0.912089662150,0.912369241124,0.912648575421,0.912927665605,0.913206512243,
    0.913485115897,0.913763477126,0.914041596489,0.914319474543,0.914597111840,
    0.914874508933,0.915151666371,0.915428584703,0.915705264474,0.915981706227,
    0.916257910505,0.916533877847,0.916809608791,0.917085103872,0.917360363623,
    0.917635388577,0.917910179264,0.918184736210,0.918459059943,0.918733150984,
    0.919007009858,0.919280637083,0.919554033178,0.919827198659,0.920100134040,
    0.920372839834,0.920645316552,0.920917564702,0.921189584791,0.921461377324,
    0.921732942804,0.922004281733,0.922275394611,0.922546281935,0.922816944201,
    0.923087381904,0.923357595536,0.923627585587,0.923897352547,0.924166896902,
    0.924436219139,0.924705319741,0.924974199190,0.925242857966,0.925511296547,
    0.925779515412,0.926047515034,0.926315295887,0.926582858444,0.926850203174,
    0.927117330547,0.927384241028,0.927650935082,0.927917413175,0.928183675767,
    0.928449723320,0.928715556291,0.928981175137,0.929246580316,0.929511772280,
    0.929776751481,0.930041518372,0.930306073400,0.930570417014,0.930834549660,
    0.931098471783,0.931362183825,0.931625686229,0.931888979433,0.932152063878,
    0.932414939999,0.932677608233,0.932940069014,0.933202322773,0.933464369943,
    0.933726210953,0.933987846231,0.934249276203,0.934510501296,0.934771521932,
    0.935032338535,0.935292951526,0.935553361323,0.935813568345,0.936073573009,
    0.936333375730,0.936592976923,0.936852376999,0.937111576370,0.937370575445,
    0.937629374634,0.937887974343,0.938146374978,0.938404576944,0.938662580642,
    0.938920386476,0.939177994846,0.939435406150,0.939692620786,0.939949639151,
    0.940206461640,0.940463088646,0.940719520563,0.940975757781,0.941231800690,
    0.941487649680,0.941743305137,0.941998767447,0.942254036996,0.942509114167,
    0.942763999343,0.943018692904,0.943273195231,0.943527506703,0.943781627695,
    0.944035558586,0.944289299750,0.944542851560,0.944796214390,0.945049388611,
    0.945302374593,0.945555172704,0.945807783314,0.946060206788,0.946312443492,
    0.946564493791,0.946816358047,0.947068036622,0.947319529878,0.947570838175,
    0.947821961869,0.948072901321,0.948323656884,0.948574228916,0.948824617769,
    0.949074823796,0.949324847351,0.949574688782,0.949824348441,0.950073826675,
    0.950323123832,0.950572240258,0.950821176298,0.951069932298,0.951318508599,
    0.951566905544,0.951815123474,0.952063162729,0.952311023648,0.952558706568,
    0.952806211827,0.953053539759,0.953300690701,0.953547664985,0.953794462944,
    0.954041084910,0.954287531213,0.954533802183,0.954779898149,0.955025819438,
    0.955271566376,0.955517139290,0.955762538504,0.956007764341,0.956252817125,
    0.956497697176,0.956742404815,0.956986940363,0.957231304137,0.957475496456,
    0.957719517636,0.957963367993,0.958207047842,0.958450557498,0.958693897272,
    0.958937067477,0.959180068425,0.959422900425,0.959665563786,0.959908058818,
    0.960150385826,0.960392545119,0.960634537001,0.960876361777,0.961118019751,
    0.961359511226,0.961600836503,0.961841995884,0.962082989669,0.962323818158,
    0.962564481648,0.962804980437,0.963045314822,0.963285485099,0.963525491562,
    0.963765334506,0.964005014224,0.964244531008,0.964483885150,0.964723076939,
    0.964962106667,0.965200974622,0.965439681092,0.965678226364,0.965916610725,
    0.966154834460,0.966392897854,0.966630801191,0.966868544754,0.967106128826,
    0.967343553688,0.967580819620,0.967817926902,0.968054875815,0.968291666634,
    0.968528299640,0.968764775107,0.969001093311,0.969237254529,0.969473259034,
    0.969709107099,0.969944798998,0.970180335002,0.970415715383,0.970650940411,
    0.970886010355,0.971120925485,0.971355686068,0.971590292373,0.971824744665,
    0.972059043211,0.972293188275,0.972527180122,0.972761019016,0.972994705219,
    0.973228238994,0.973461620602,0.973694850304,0.973927928359,0.974160855027,
    0.974393630567,0.974626255236,0.974858729290,0.975091052988,0.975323226584,
    0.975555250333,0.975787124489,0.976018849306,0.976250425037,0.976481851933,
    0.976713130247,0.976944260229,0.977175242128,0.977406076195,0.977636762678,
    0.977867301825,0.978097693883,0.978327939100,0.978558037720,0.978787989989,
    0.979017796151,0.979247456452,0.979476971133,0.979706340438,0.979935564608,
    0.980164643886,0.980393578510,0.980622368722,0.980851014761,0.981079516865,
    0.981307875273,0.981536090222,0.981764161948,0.981992090688,0.982219876677,
    0.982447520151,0.982675021342,0.982902380486,0.983129597815,0.983356673561,
    0.983583607956,0.983810401231,0.984037053616,0.984263565343,0.984489936638,
    0.984716167733,0.984942258853,0.985168210228,0.985394022083,0.985619694646,
    0.985845228141,0.986070622793,0.986295878828,0.986520996469,0.986745975939,
    0.986970817460,0.987195521256,0.987420087548,0.987644516555,0.987868808500,
    0.988092963601,0.988316982078,0.988540864149,0.988764610033,0.988988219947,
    0.989211694108,0.989435032732,0.989658236034,0.989881304232,0.990104237538,
    0.990327036167,0.990549700332,0.990772230247,0.990994626125,0.991216888176,
    0.991439016613,0.991661011645,0.991882873485,0.992104602340,0.992326198421,
    0.992547661936,0.992768993093,0.992990192099,0.993211259163,0.993432194490,
    0.993652998286,0.993873670757,0.994094212108,0.994314622543,0.994534902266,
    0.994755051480,0.994975070389,0.995194959195,0.995414718100,0.995634347304,
    0.995853847009,0.996073217416,0.996292458723,0.996511571130,0.996730554837,
    0.996949410041,0.997168136940,0.997386735732,0.997605206612,0.997823549779,
    0.998041765427,0.998259853752,0.998477814949,0.998695649212,0.998913356734,
    0.999130937710,0.999348392333,0.999565720794,0.999782923286,1.000000000000,
  };

  if (t > .999999) 		/* Asymtotic forms */
    return (8.0 + t) / 9.0;
  if (t < -.99999)		
    return (1 + sqrt(t+1.)/3.0)/2.0;

  /* Use linear interpolation between table entries. */

  /* convert t to a table index */
           /* Table is 1025 elements long with a delta of 1/512 */
  tprime = ((t+1.0)*512); 
  it = (int) tprime;

  /* Use linear interpolation between table entries. */
  low = table[it];
  high = table[it+1];
  u = (tprime - (double)it);
  v = (1.0 - u);
  return ( low*v + high*u );
}


static double sink_lookup(double t)
{
  register int it;
  register double tprime, low, high,u, v;
  static double table[1025] = {	/* Precalculated table of sin( arccos(t)/3 ) */
    0.866025403784,0.855419800875,0.850915249360,0.847413998676,0.844431892606,
    0.841781415518,0.839366406841,0.837129747912,0.835034226047,0.833053995473,
    0.831170239654,0.829368754829,0.827638507010,0.825970722171,0.824358286943,
    0.822795339363,0.821276980817,0.819799068011,0.818358059356,0.816950899332,
    0.815574929943,0.814227821884,0.812907520318,0.811612201635,0.810340238593,
    0.809090171937,0.807860687085,0.806650594818,0.805458815151,0.804284363793,
    0.803126340679,0.801983920214,0.800856342929,0.799742908291,0.798642968496,
    0.797555923075,0.796481214191,0.795418322517,0.794366763613,0.793326084735,
    0.792295862001,0.791275697883,0.790265218966,0.789264073952,0.788271931863,
    0.787288480438,0.786313424680,0.785346485551,0.784387398788,0.783435913827,
    0.782491792834,0.781554809813,0.780624749800,0.779701408120,0.778784589714,
    0.777874108514,0.776969786875,0.776071455051,0.775178950710,0.774292118488,
    0.773410809578,0.772534881349,0.771664196992,0.770798625191,0.769938039820,
    0.769082319662,0.768231348142,0.767385013082,0.766543206473,0.765705824261,
    0.764872766148,0.764043935399,0.763219238675,0.762398585861,0.761581889917,
    0.760769066730,0.759960034977,0.759154716001,0.758353033684,0.757554914338,
    0.756760286597,0.755969081313,0.755181231461,0.754396672051,0.753615340040,
    0.752837174252,0.752062115301,0.751290105517,0.750521088880,0.749755010949,
    0.748991818808,0.748231461000,0.747473887473,0.746719049527,0.745966899763,
    0.745217392036,0.744470481406,0.743726124095,0.742984277446,0.742244899884,
    0.741507950874,0.740773390889,0.740041181372,0.739311284704,0.738583664170,
    0.737858283934,0.737135109003,0.736414105204,0.735695239156,0.734978478241,
    0.734263790584,0.733551145030,0.732840511114,0.732131859047,0.731425159694,
    0.730720384549,0.730017505722,0.729316495914,0.728617328407,0.727919977040,
    0.727224416195,0.726530620783,0.725838566225,0.725148228441,0.724459583834,
    0.723772609274,0.723087282091,0.722403580056,0.721721481372,0.721040964662,
    0.720362008957,0.719684593685,0.719008698659,0.718334304071,0.717661390477,
    0.716989938792,0.716319930275,0.715651346527,0.714984169476,0.714318381373,
    0.713653964782,0.712990902572,0.712329177909,0.711668774253,0.711009675342,
    0.710351865194,0.709695328097,0.709040048599,0.708386011508,0.707733201881,
    0.707081605021,0.706431206469,0.705781992001,0.705133947620,0.704487059552,
    0.703841314241,0.703196698344,0.702553198727,0.701910802457,0.701269496801,
    0.700629269222,0.699990107371,0.699351999086,0.698714932387,0.698078895472,
    0.697443876713,0.696809864652,0.696176847999,0.695544815626,0.694913756566,
    0.694283660009,0.693654515297,0.693026311923,0.692399039525,0.691772687889,
    0.691147246938,0.690522706737,0.689899057484,0.689276289510,0.688654393277,
    0.688033359375,0.687413178517,0.686793841541,0.686175339403,0.685557663180,
    0.684940804061,0.684324753351,0.683709502465,0.683095042929,0.682481366373,
    0.681868464535,0.681256329255,0.680644952473,0.680034326231,0.679424442665,
    0.678815294011,0.678206872596,0.677599170839,0.676992181252,0.676385896433,
    0.675780309070,0.675175411936,0.674571197887,0.673967659864,0.673364790888,
    0.672762584059,0.672161032558,0.671560129642,0.670959868643,0.670360242969,
    0.669761246100,0.669162871588,0.668565113059,0.667967964203,0.667371418783,
    0.666775470627,0.666180113631,0.665585341754,0.664991149020,0.664397529516,
    0.663804477391,0.663211986855,0.662620052177,0.662028667685,0.661437827766,
    0.660847526864,0.660257759478,0.659668520163,0.659079803528,0.658491604236,
    0.657903917002,0.657316736593,0.656730057828,0.656143875574,0.655558184750,
    0.654972980322,0.654388257304,0.653804010758,0.653220235792,0.652636927558,
    0.652054081257,0.651471692130,0.650889755466,0.650308266592,0.649727220882,
    0.649146613749,0.648566440648,0.647986697075,0.647407378565,0.646828480692,
    0.646249999069,0.645671929350,0.645094267221,0.644517008410,0.643940148679,
    0.643363683826,0.642787609687,0.642211922128,0.641636617055,0.641061690404,
    0.640487138147,0.639912956286,0.639339140858,0.638765687932,0.638192593607,
    0.637619854014,0.637047465315,0.636475423702,0.635903725396,0.635332366649,
    0.634761343742,0.634190652982,0.633620290706,0.633050253281,0.632480537097,
    0.631911138574,0.631342054158,0.630773280322,0.630204813563,0.629636650405,
    0.629068787397,0.628501221114,0.627933948153,0.627366965137,0.626800268712,
    0.626233855550,0.625667722343,0.625101865807,0.624536282681,0.623970969727,
    0.623405923728,0.622841141488,0.622276619834,0.621712355614,0.621148345695,
    0.620584586967,0.620021076339,0.619457810740,0.618894787120,0.618332002446,
    0.617769453706,0.617207137908,0.616645052076,0.616083193255,0.615521558507,
    0.614960144912,0.614398949568,0.613837969590,0.613277202111,0.612716644281,
    0.612156293267,0.611596146252,0.611036200436,0.610476453035,0.609916901280,
    0.609357542418,0.608798373715,0.608239392447,0.607680595908,0.607121981407,
    0.606563546268,0.606005287828,0.605447203440,0.604889290470,0.604331546298,
    0.603773968319,0.603216553940,0.602659300583,0.602102205683,0.601545266687,
    0.600988481056,0.600431846262,0.599875359793,0.599319019146,0.598762821832,
    0.598206765373,0.597650847304,0.597095065171,0.596539416532,0.595983898957,
    0.595428510027,0.594873247332,0.594318108476,0.593763091073,0.593208192747,
    0.592653411133,0.592098743875,0.591544188631,0.590989743065,0.590435404853,
    0.589881171681,0.589327041244,0.588773011248,0.588219079406,0.587665243442,
    0.587111501089,0.586557850089,0.586004288193,0.585450813160,0.584897422760,
    0.584344114769,0.583790886972,0.583237737164,0.582684663146,0.582131662728,
    0.581578733729,0.581025873975,0.580473081298,0.579920353542,0.579367688553,
    0.578815084189,0.578262538313,0.577710048797,0.577157613517,0.576605230358,
    0.576052897214,0.575500611981,0.574948372565,0.574396176878,0.573844022839,
    0.573291908371,0.572739831405,0.572187789879,0.571635781735,0.571083804924,
    0.570531857398,0.569979937121,0.569428042056,0.568876170178,0.568324319463,
    0.567772487894,0.567220673459,0.566668874152,0.566117087972,0.565565312921,
    0.565013547010,0.564461788250,0.563910034660,0.563358284264,0.562806535089,
    0.562254785167,0.561703032534,0.561151275231,0.560599511304,0.560047738801,
    0.559495955777,0.558944160289,0.558392350398,0.557840524169,0.557288679672,
    0.556736814980,0.556184928168,0.555633017317,0.555081080511,0.554529115835,
    0.553977121381,0.553425095242,0.552873035514,0.552320940296,0.551768807693,
    0.551216635809,0.550664422752,0.550112166635,0.549559865571,0.549007517677,
    0.548455121072,0.547902673878,0.547350174219,0.546797620221,0.546245010014,
    0.545692341729,0.545139613498,0.544586823457,0.544033969744,0.543481050498,
    0.542928063859,0.542375007971,0.541821880979,0.541268681028,0.540715406268,
    0.540162054846,0.539608624915,0.539055114626,0.538501522134,0.537947845592,
    0.537394083159,0.536840232990,0.536286293244,0.535732262081,0.535178137660,
    0.534623918143,0.534069601693,0.533515186471,0.532960670642,0.532406052369,
    0.531851329816,0.531296501150,0.530741564535,0.530186518137,0.529631360123,
    0.529076088659,0.528520701912,0.527965198048,0.527409575234,0.526853831637,
    0.526297965423,0.525741974759,0.525185857811,0.524629612745,0.524073237728,
    0.523516730923,0.522960090497,0.522403314614,0.521846401436,0.521289349128,
    0.520732155852,0.520174819769,0.519617339041,0.519059711827,0.518501936286,
    0.517944010577,0.517385932856,0.516827701279,0.516269314000,0.515710769173,
    0.515152064951,0.514593199483,0.514034170918,0.513474977406,0.512915617091,
    0.512356088117,0.511796388629,0.511236516767,0.510676470670,0.510116248475,
    0.509555848318,0.508995268333,0.508434506650,0.507873561399,0.507312430708,
    0.506751112699,0.506189605497,0.505627907222,0.505066015990,0.504503929917,
    0.503941647116,0.503379165696,0.502816483765,0.502253599428,0.501690510785,
    0.501127215936,0.500563712977,0.500000000000,0.499436075095,0.498871936349,
    0.498307581845,0.497743009663,0.497178217880,0.496613204570,0.496047967801,
    0.495482505642,0.494916816153,0.494350897395,0.493784747423,0.493218364288,
    0.492651746038,0.492084890717,0.491517796364,0.490950461016,0.490382882703,
    0.489815059453,0.489246989289,0.488678670230,0.488110100289,0.487541277478,
    0.486972199800,0.486402865257,0.485833271844,0.485263417553,0.484693300370,
    0.484122918276,0.483552269247,0.482981351256,0.482410162268,0.481838700243,
    0.481266963139,0.480694948905,0.480122655486,0.479550080822,0.478977222846,
    0.478404079487,0.477830648667,0.477256928303,0.476682916306,0.476108610580,
    0.475534009024,0.474959109532,0.474383909989,0.473808408276,0.473232602266,
    0.472656489828,0.472080068821,0.471503337101,0.470926292515,0.470348932903,
    0.469771256101,0.469193259934,0.468614942223,0.468036300782,0.467457333415,
    0.466878037922,0.466298412093,0.465718453713,0.465138160557,0.464557530395,
    0.463976560986,0.463395250085,0.462813595435,0.462231594776,0.461649245835,
    0.461066546333,0.460483493984,0.459900086492,0.459316321552,0.458732196852,
    0.458147710071,0.457562858879,0.456977640936,0.456392053895,0.455806095400,
    0.455219763083,0.454633054571,0.454045967479,0.453458499412,0.452870647967,
    0.452282410730,0.451693785280,0.451104769183,0.450515359996,0.449925555267,
    0.449335352533,0.448744749319,0.448153743144,0.447562331511,0.446970511917,
    0.446378281845,0.445785638770,0.445192580154,0.444599103448,0.444005206092,
    0.443410885516,0.442816139136,0.442220964359,0.441625358579,0.441029319178,
    0.440432843526,0.439835928981,0.439238572891,0.438640772588,0.438042525393,
    0.437443828616,0.436844679552,0.436245075485,0.435645013684,0.435044491407,
    0.434443505896,0.433842054383,0.433240134084,0.432637742202,0.432034875926,
    0.431431532432,0.430827708879,0.430223402416,0.429618610173,0.429013329270,
    0.428407556809,0.427801289877,0.427194525549,0.426587260881,0.425979492917,
    0.425371218683,0.424762435190,0.424153139433,0.423543328393,0.422932999031,
    0.422322148295,0.421710773115,0.421098870405,0.420486437060,0.419873469961,
    0.419259965970,0.418645921931,0.418031334672,0.417416201002,0.416800517713,
    0.416184281577,0.415567489350,0.414950137767,0.414332223547,0.413713743388,
    0.413094693969,0.412475071949,0.411854873971,0.411234096653,0.410612736597,
    0.409990790383,0.409368254570,0.408745125699,0.408121400287,0.407497074831,
    0.406872145808,0.406246609672,0.405620462855,0.404993701769,0.404366322800,
    0.403738322315,0.403109696656,0.402480442145,0.401850555076,0.401220031724,
    0.400588868338,0.399957061143,0.399324606340,0.398691500106,0.398057738592,
    0.397423317925,0.396788234205,0.396152483509,0.395516061885,0.394878965358,
    0.394241189923,0.393602731550,0.392963586183,0.392323749736,0.391683218097,
    0.391041987125,0.390400052652,0.389757410479,0.389114056381,0.388469986100,
    0.387825195352,0.387179679819,0.386533435158,0.385886456989,0.385238740906,
    0.384590282469,0.383941077207,0.383291120616,0.382640408160,0.381988935271,
    0.381336697347,0.380683689751,0.380029907814,0.379375346832,0.378720002064,
    0.378063868737,0.377406942040,0.376749217126,0.376090689113,0.375431353080,
    0.374771204070,0.374110237088,0.373448447099,0.372785829031,0.372122377772,
    0.371458088171,0.370792955035,0.370126973132,0.369460137188,0.368792441888,
    0.368123881872,0.367454451741,0.366784146051,0.366112959314,0.365440885997,
    0.364767920524,0.364094057271,0.363419290571,0.362743614707,0.362067023917,
    0.361389512390,0.360711074269,0.360031703644,0.359351394557,0.358670141002,
    0.357987936919,0.357304776197,0.356620652674,0.355935560134,0.355249492307,
    0.354562442869,0.353874405442,0.353185373590,0.352495340822,0.351804300591,
    0.351112246288,0.350419171250,0.349725068750,0.349029932003,0.348333754163,
    0.347636528321,0.346938247506,0.346238904682,0.345538492749,0.344837004543,
    0.344134432830,0.343430770313,0.342726009624,0.342020143326,0.341313163913,
    0.340605063807,0.339895835359,0.339185470845,0.338473962468,0.337761302356,
    0.337047482561,0.336332495055,0.335616331736,0.334898984418,0.334180444837,
    0.333460704645,0.332739755414,0.332017588627,0.331294195687,0.330569567905,
    0.329843696506,0.329116572627,0.328388187312,0.327658531514,0.326927596091,
    0.326195371809,0.325461849335,0.324727019238,0.323990871989,0.323253397959,
    0.322514587414,0.321774430518,0.321032917329,0.320290037796,0.319545781761,
    0.318800138955,0.318053098997,0.317304651390,0.316554785523,0.315803490665,
    0.315050755968,0.314296570459,0.313540923045,0.312783802504,0.312025197488,
    0.311265096519,0.310503487986,0.309740360144,0.308975701114,0.308209498875,
    0.307441741266,0.306672415984,0.305901510578,0.305129012450,0.304354908850,
    0.303579186876,0.302801833469,0.302022835410,0.301242179321,0.300459851657,
    0.299675838706,0.298890126587,0.298102701245,0.297313548448,0.296522653784,
    0.295730002659,0.294935580292,0.294139371714,0.293341361760,0.292541535070,
    0.291739876084,0.290936369037,0.290130997957,0.289323746659,0.288514598742,
    0.287703537586,0.286890546347,0.286075607952,0.285258705095,0.284439820232,
    0.283618935577,0.282796033099,0.281971094513,0.281144101277,0.280315034590,
    0.279483875380,0.278650604306,0.277815201746,0.276977647795,0.276137922260,
    0.275296004650,0.274451874172,0.273605509728,0.272756889902,0.271905992958,
    0.271052796831,0.270197279123,0.269339417091,0.268479187643,0.267616567330,
    0.266751532336,0.265884058474,0.265014121173,0.264141695472,0.263266756011,
    0.262389277022,0.261509232320,0.260626595292,0.259741338887,0.258853435608,
    0.257962857501,0.257069576142,0.256173562627,0.255274787562,0.254373221050,
    0.253468832678,0.252561591506,0.251651466052,0.250738424282,0.249822433593,
    0.248903460798,0.247981472116,0.247056433151,0.246128308882,0.245197063642,
    0.244262661103,0.243325064260,0.242384235410,0.241440136137,0.240492727291,
    0.239541968968,0.238587820489,0.237630240379,0.236669186348,0.235704615264,
    0.234736483130,0.233764745062,0.232789355263,0.231810266992,0.230827432545,
    0.229840803219,0.228850329287,0.227855959966,0.226857643385,0.225855326554,
    0.224848955325,0.223838474364,0.222823827106,0.221804955722,0.220781801076,
    0.219754302685,0.218722398675,0.217686025734,0.216645119068,0.215599612349,
    0.214549437667,0.213494525472,0.212434804520,0.211370201818,0.210300642557,
    0.209226050054,0.208146345680,0.207061448795,0.205971276673,0.204875744426,
    0.203774764922,0.202668248704,0.201556103901,0.200438236136,0.199314548429,
    0.198184941094,0.197049311635,0.195907554631,0.194759561619,0.193605220971,
    0.192444417760,0.191277033625,0.190102946623,0.188922031077,0.187734157415,
    0.186539191994,0.185336996926,0.184127429881,0.182910343886,0.181685587110,
    0.180453002637,0.179212428225,0.177963696045,0.176706632414,0.175441057503,
    0.174166785025,0.172883621910,0.171591367953,0.170289815439,0.168978748742,
    0.167657943899,0.166327168148,0.164986179442,0.163634725919,0.162272545334,
    0.160899364454,0.159514898400,0.158118849944,0.156710908740,0.155290750506,
    0.153858036130,0.152412410702,0.150953502465,0.149480921678,0.147994259368,
    0.146493085982,0.144976949904,0.143445375838,0.141897863032,0.140333883329,
    0.138752879024,0.137154260494,0.135537403585,0.133901646711,0.132246287640,
    0.130570579910,0.128873728829,0.127154886999,0.125413149293,0.123647547196,
    0.121857042415,0.120040519634,0.118196778275,0.116324523091,0.114422353374,
    0.112488750538,0.110522063754,0.108520493245,0.106482070782,0.104404636752,
    0.102285813064,0.100122970908,0.097913192126,0.095653222583,0.093339415406,
    0.090967661269,0.088533301911,0.086031021684,0.083454709893,0.080797283708,
    0.078050456881,0.075204432527,0.072247487062,0.069165394090,0.065940605898,
    0.062551054850,0.058968333762,0.055154809898,0.051058792051,0.046605855187,
    0.041681762316,0.036094192631,0.029468115200,0.020835217922,0.000000000000
    };
  if (t > .999999) 		/* Asymtotic forms */
    return sqrt(2.0*(1.0 - t)/9.0);
  if (t < -.99999)		
    return ( SQRT3 - sqrt(2.0*(t+1))/3.0)/2.0;
    
    
  /* convert t to a table index */
           /* Table is 1025 elements long with a delta of 1/512 */
  tprime = ((t+1.0)*512);
  it = (int) tprime;

  /* Use linear interpolation between table entries. */
  low = table[it];
  high = table[it+1];
  u = (tprime - (double)it);
  v = (1.0 - u);
  return ( low*v + high*u );
}
#endif


#if 0
/*
 * Alternate version -- simpler but less robust and less efficient.
 * from Gems I.
 */

/*
 *  Roots3And4.c
 *
 *  Utility functions to find cubic and quartic roots,
 *  coefficients are passed like this:
 *
 *      c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
 *
 *  The functions return the number of non-complex roots and
 *  put the values into the s array.
 *
 *  Author:         Jochen Schwarze (schwarze@isa.de)
 *
 *  Jan 26, 1990    Version for Graphics Gems
 *  Oct 11, 1990    Fixed sign problem for negative q's in SolveQuartic
 *  	    	    (reported by Mark Podlipec),
 *  	    	    Old-style function definitions,
 *  	    	    IsZero() as a macro
 *  Nov 23, 1990    Some systems do not declare acos() and cbrt() in
 *                  <math.h>, though the functions exist in the library.
 *                  If large coefficients are used, EQN_EPS should be
 *                  reduced considerably (e.g. to 1E-30), results will be
 *                  correct but multiple roots might be reported more
 *                  than once.
 */

#include    <math.h>
#ifndef M_PI
#define M_PI          3.14159265358979323846
#endif
extern double  sqrt( double x );
extern double  cbrt( double x );
extern double cos( double x ); 
extern double acos( double x );

/* epsilon surrounding for near zero values */

#define     EQN_EPS     1e-9
#define	    IsZero(x)	((x) > -EQN_EPS && (x) < EQN_EPS)

#ifdef NOCBRT
#define     cbrt(x)     ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \
                          ((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0))
#endif

int SolveQuadric( double c[3], double s[2] )
{
    double p, q, D;

    /* normal form: x^2 + px + q = 0 */

    p = c[ 1 ] / (2 * c[ 2 ]);
    q = c[ 0 ] / c[ 2 ];

    D = p * p - q;

    if (IsZero(D))
    {
	s[ 0 ] = - p;
	return 1;
    }
    else if (D < 0)
    {
	return 0;
    }
    else if (D > 0)
    {
	double sqrt_D = sqrt(D);

	s[ 0 ] =   sqrt_D - p;
	s[ 1 ] = - sqrt_D - p;
	return 2;
    } else {
		return 0;
	}
}


int SolveCubic( double c[4], double s[3] )
{
    int     i, num;
    double  sub;
    double  A, B, C;
    double  sq_A, p, q;
    double  cb_p, D;

    /* normal form: x^3 + Ax^2 + Bx + C = 0 */

    A = c[ 2 ] / c[ 3 ];
    B = c[ 1 ] / c[ 3 ];
    C = c[ 0 ] / c[ 3 ];

    /*  substitute x = y - A/3 to eliminate quadric term:
	x^3 +px + q = 0 */

    sq_A = A * A;
    p = 1.0/3 * (- 1.0/3 * sq_A + B);
    q = 1.0/2 * (2.0/27 * A * sq_A - 1.0/3 * A * B + C);

    /* use Cardano's formula */

    cb_p = p * p * p;
    D = q * q + cb_p;

    if (IsZero(D))
    {
	if (IsZero(q)) /* one triple solution */
	{
	    s[ 0 ] = 0;
	    num = 1;
	}
	else /* one single and one double solution */
	{
	    double u = cbrt(-q);
	    s[ 0 ] = 2 * u;
	    s[ 1 ] = - u;
	    num = 2;
	}
    }
    else if (D < 0) /* Casus irreducibilis: three real solutions */
    {
	double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
	double t = 2 * sqrt(-p);

	s[ 0 ] =   t * cos(phi);
	s[ 1 ] = - t * cos(phi + M_PI / 3);
	s[ 2 ] = - t * cos(phi - M_PI / 3);
	num = 3;
    }
    else /* one real solution */
    {
	double sqrt_D = sqrt(D);
	double u = cbrt(sqrt_D - q);
	double v = - cbrt(sqrt_D + q);

	s[ 0 ] = u + v;
	num = 1;
    }

    /* resubstitute */

    sub = 1.0/3 * A;

    for (i = 0; i < num; ++i)
	s[ i ] -= sub;

    return num;
}


int SolveQuartic( double c[5], double s[4] )
{
    double  coeffs[ 4 ];
    double  z, u, v, sub;
    double  A, B, C, D;
    double  sq_A, p, q, r;
    int     i, num;

    /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */

    A = c[ 3 ] / c[ 4 ];
    B = c[ 2 ] / c[ 4 ];
    C = c[ 1 ] / c[ 4 ];
    D = c[ 0 ] / c[ 4 ];

    /*  substitute x = y - A/4 to eliminate cubic term:
	x^4 + px^2 + qx + r = 0 */

    sq_A = A * A;
    p = - 3.0/8 * sq_A + B;
    q = 1.0/8 * sq_A * A - 1.0/2 * A * B + C;
    r = - 3.0/256*sq_A*sq_A + 1.0/16*sq_A*B - 1.0/4*A*C + D;

    if (IsZero(r))
    {
	/* no absolute term: y(y^3 + py + q) = 0 */

	coeffs[ 0 ] = q;
	coeffs[ 1 ] = p;
	coeffs[ 2 ] = 0;
	coeffs[ 3 ] = 1;

	num = SolveCubic(coeffs, s);

	s[ num++ ] = 0;
    }
    else
    {
	/* solve the resolvent cubic ... */

	coeffs[ 0 ] = 1.0/2 * r * p - 1.0/8 * q * q;
	coeffs[ 1 ] = - r;
	coeffs[ 2 ] = - 1.0/2 * p;
	coeffs[ 3 ] = 1;

	(void) SolveCubic(coeffs, s);

	/* ... and take the one real solution ... */

	z = s[ 0 ];

	/* ... to build two quadric equations */

	u = z * z - r;
	v = 2 * z - p;

	if (IsZero(u))
	    u = 0;
	else if (u > 0)
	    u = sqrt(u);
	else
	    return 0;

	if (IsZero(v))
	    v = 0;
	else if (v > 0)
	    v = sqrt(v);
	else
	    return 0;

	coeffs[ 0 ] = z - u;
	coeffs[ 1 ] = q < 0 ? -v : v;
	coeffs[ 2 ] = 1;

	num = SolveQuadric(coeffs, s);

	coeffs[ 0 ]= z + u;
	coeffs[ 1 ] = q < 0 ? v : -v;
	coeffs[ 2 ] = 1;

	num += SolveQuadric(coeffs, s + num);
    }

    /* resubstitute */

    sub = 1.0/4 * A;

    for (i = 0; i < num; ++i)
	s[ i ] -= sub;

    return num;
}

#endif

/*
 * Copyright (c) 1990, Graphics and AI Laboratory, University of Washington
 * Copying, use and development for non-commercial purposes permitted.
 *                  All rights for commercial use reserved.
 */
